Skip to content

fix(config): prevent YAML structure injection via env var substitution#5091

Open
MikeGoldsmith wants to merge 4 commits intoopen-telemetry:mainfrom
MikeGoldsmith:mike/config-file-yaml-injection
Open

fix(config): prevent YAML structure injection via env var substitution#5091
MikeGoldsmith wants to merge 4 commits intoopen-telemetry:mainfrom
MikeGoldsmith:mike/config-file-yaml-injection

Conversation

@MikeGoldsmith
Copy link
Copy Markdown
Member

@MikeGoldsmith MikeGoldsmith commented Apr 14, 2026

Description

Fixes a YAML structure injection vulnerability in declarative file configuration's environment variable substitution (_env_substitution.py).

The substitute_env_vars() function performed raw string substitution of env var values into YAML text before parsing. A value containing a newline followed by valid YAML (e.g. "legit-service\nmalicious_key: injected_value") would inject extra keys at parse time.

Example:

SERVICE_NAME="legit-service\nmalicious_key: injected_value"

Config template:

file_format: "0.1"
service_name: ${SERVICE_NAME}

After substitution, before yaml.safe_load:

file_format: "0.1"
service_name: legit-service
malicious_key: injected_value    # ← injected

The spec explicitly requires: "It MUST NOT be possible to inject YAML structures by environment variables."

Fix

Values containing \n or \r are now emitted as YAML double-quoted scalars with escaped newlines:

escaped = value.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n")...
return f'"{escaped}"'

Simple values (no newlines) are returned as-is. This is intentional — the spec also states "Node types MUST be interpreted after environment variable substitution takes place", meaning type coercion must be preserved.

Why not yaml.dump(value)?

yaml.dump() was considered but has two problems:

  1. Breaks type coercionyaml.dump("true") produces 'true' (single-quoted string), so env var values like true, false, 42, 3.14, null would all parse as strings instead of their native YAML types, violating the spec's type coercion requirement.

  2. Produces multi-line output for newline-containing valuesyaml.dump("a\nb") produces a multi-line block scalar that may be structurally invalid when substituted inline into a template. The custom escaping always produces a single-line "a\nb" double-quoted scalar.

Value yaml.dump result Custom approach result
"true" str 'true' bool True
"42" str '42' int 42
"null" str 'null' NoneType None
"my-service" str 'my-service' str 'my-service'
"a\nb" multi-line block scalar ✗ "a\nb" single-line ✓

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

Added tests in test_env_substitution.py:

  • test_newline_in_value_prevents_yaml_injection — verifies the exact PoC is blocked
  • test_newline_in_value_preserved_as_literal — verifies the newline is preserved as a literal character in the parsed value
  • test_carriage_return_in_value_is_escaped — covers \r case
  • test_type_coercion_preserved_for_simple_values — verifies truebool, 42int still work

Does This PR Require a Contrib Repo Change?

  • No.

Checklist:

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added
  • Documentation has been updated

…itution

Environment variable values containing newlines were substituted verbatim
into YAML text before parsing, allowing a value like
"legit-service\nmalicious_key: injected" to create extra YAML keys.

The spec explicitly requires: "It MUST NOT be possible to inject YAML
structures by environment variables." Fix wraps values containing \n or \r
in YAML double-quoted scalars. Simple values are returned as-is so that
YAML type coercion still applies per "Node types MUST be interpreted after
environment variable substitution takes place."

Assisted-by: Claude Sonnet 4.6
@MikeGoldsmith MikeGoldsmith moved this to Ready for review in Python PR digest Apr 14, 2026
Assisted-by: Claude Sonnet 4.6
@MikeGoldsmith
Copy link
Copy Markdown
Member Author

Check links is failing because of a bad changelog entry PR path, will be fixed in:

@github-project-automation github-project-automation bot moved this from Ready for review to Approved PRs in Python PR digest Apr 14, 2026
Copy link
Copy Markdown
Contributor

@tammy-baylis-swi tammy-baylis-swi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Approved PRs

Development

Successfully merging this pull request may close these issues.

3 participants